/**
 * \file  clients_manager.h
 * \brief Definition of ClientsManager class.
 *
 * FuD: FuDePAN Ubiquitous Distribution, a framework for work distribution.
 * <http://fud.googlecode.com/>
 * Copyright (C) 2009, 2010, 2011 - Guillermo Biset & Mariano Bessone & Emanuel Bringas, FuDePAN
 *
 * This file is part of the FuD project.
 *
 * Contents:       Header file for FuD providing class DistributableJob.
 *
 * System:         FuD
 * Homepage:       <http://fud.googlecode.com/>
 * Language:       C++
 *
 * @author     Guillermo Biset
 * @email      billybiset AT gmail.com
 *  
 * @author     Mariano Bessone
 * @email      marjobe AT gmail.com
 *
 * @author     Emanuel Bringas
 * @email      emab73 AT gmail.com
 *
 * FuD is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FuD is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with FuD.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef CLIENTS_MANAGER_H
#define CLIENTS_MANAGER_H

#include <map>
#include <boost/thread.hpp>

#include "client_proxy.h"
#include "fud/common/job_unit.h"
#include "fud/server/events.h"

namespace fud
{
    /**
     * Interface for the listener of Events coming from the ClientsManager.
     *
     * \sa ClientsManager
     * \sa Event
     */
    struct ClientsManagerListener
    {
        /**
         * Signals when a client is ready to handle another JobUnit.
         */
        virtual void free_client_event()                                          = 0;

        /**
         * Signals completion of a JobUnit.
         * @param id : The ID of the completed JobUnit.
         *
         * \sa JobUnit
         */
        virtual void job_unit_completed_event(JobUnitID id) = 0;

        /**
         * Signals a incoming message from a JobUnit.
         *
         * @param id : the ID of the JobUnit.
         * @param message_number : the number of the message.
         * @param message : the incoming message.
         *
         */
        virtual void incoming_message_event(JobUnitID id, fud_uint message_number, std::string* message) = 0;
    };

    /**
     * An interface for handling registration and communication with clients.
     * Also, the method create_clients_manager is declared here, which will
     * later be implemented in the implementation file in one of ClientsManager
     * descendants.
     */
    class ClientsManager
    {
        public:
            /* Interface for base classes, can't put it protected */
            /**
             * Informs completion of a JobUnit. It will generate an event signaling
             * this fact.
             * @param id : The ID of the completed JobUnit.
             *
             * \sa JobUnit
             */
            void inform_completion(JobUnitID id);

            /**
             * Informs a incoming message from id-th JobUnit.
             *
             * @param id : the ID of the JobUnit.
             * @param message_number : the number of the message.
             * @param message : the incoming message.
             *
             */
            void inform_incoming_message(JobUnitID id, fud_uint message_number, std::string* message);

            /**
             * Tells the listener that a client is free.
             */
            void free_client_event();

            // old load balancer method
            // float get_size_to_ms_to_process_ratio() const;

            /**
             * Singleton method.
             *
             * @returns The only instance of ClientsManager in the system.
             */
            inline static ClientsManager* get_instance() {return _instance;}

            /**
             * Disconnects a proxy from the system.
             * @param id : the client proxy ID that needs to be disconnected.
             *
             * \sa ClientProxy
             * \sa register_client
             */
            void  deregister_client(ClientID id);

            /* Interface for Job Manager */
            /**
             * Set the listener of events generated by this instance.
             *
             * \sa Event
             * \sa ClientsManagerListener
             */
            void set_listener(ClientsManagerListener* const listener);

            /**
             * Tries to assign a JobUnit to a client.
             *
             * @returns <b>true</b> if the JobUnit was succesfully assigned
             *          to a free client, <b>false</b> otherwise.
             *
             * \sa JobUnit
             */
            bool assign_job_unit  (const JobUnit& job_unit);

            /**
             * Handles the "FreeClients" request. This method returns the number of free
             * clients that the server is abble to give us.
             *
             * @param clients_requested : the number of clients that a client request.
             *
             * @returns the number of free clients that the server is abble to give us.
             */
            virtual fud_uint handle_free_clients_request(fud_uint clients_requested);

            /**
             * Gets the id-th client.
             *
             * @param id : the id of the client you want.
             *
             * @returns the client proxy with ID id.
             */
            virtual ClientProxy* get_client(ClientID id);

            /**
             * Place orders for idle clients.
             *
             * @param id : the client ID who place orders.
             * @param count : number of orders to reserve.
             */
            void place_orders(fud_uint count);

            virtual ~ClientsManager();
        protected:
            /**
             * Standard constructor. Initializes the lists and singleton attributes.
             */
            ClientsManager();

            /**
             * Get a free client.
             * Used while trying to assign a JobUnit to a free client.
             *
             * @returns A pointer to a ClientProxy in a <b>free</b> state, or <b>NULL</b>
             *          if there is none.
             *
             * \sa ClientProxy
             */
            virtual ClientProxy* get_available_client();

            /**
             * Connects a proxy to the system.
             *
             * @param client : The client proxy that needs to be connected.
             *
             * \sa ClientProxy
             * \sa register_client
             */
            void  register_client  (ClientProxy* client);

        private:

            void update_time_average(JobUnitSize ju_size, uint32_t ms_elapsed);

            std::map<ClientID, ClientProxy*>    _client_proxies;
            boost::mutex                        _client_proxies_mutex;

            size_t                          _completed_job_units;


            static ClientsManager*          _instance;

            ClientsManagerListener*         _listener;

            /* Orders management */

            /**
             * Place an order reservation.
             *
             * @param id : the client ID who place the order.
             */
            void place_an_order();

            /**
             * Take an order reservation.
             */
            void take_an_order();

            /**
             * Number of order reservations.
             *
             * @returns the number of current reservations.
             */
            fud_uint orders();

            /* Attributes about reservation. */
            fud_uint    _free_clients;
            fud_uint    _available_clients;
            fud_uint    _reservations;

    };

    /**
      * To be implemented, will be linked with the apropriate concrete
      * ClientsManager's method.
      */
    ClientsManager* create_clients_manager();
}

#endif
